//==============================================================================
//
//          DTCPClient - the TCP client class in the ofc-library
//
//               Copyright (C) 2004  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2008-08-11 16:59:46 $ $Revision: 1.10 $

//==============================================================================

#include "ofc/DTCPClient.h"


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DSocketAddress.h"
#include "ofc/DSocket.h"



//
// The DTCPClient class implements a number of methods for implementing a TCP client.
//
// @example
// #include <stdio.h>
// #include "ofc/DTCPClient.h"
// #include "ofc/DSocketAddress.h"
// 
// int main(int argc, char *argv[])
// {
//   DInetSocketAddress *addr = [DInetSocketAddress new];
//   DTCPClient         *clnt = [DTCPClient         new];
//   DData              *data;
// 
//   [addr host :"localhost" :7000];
// 
//   printf("Open the client..\n");
// 
//   if ([clnt open :[addr family] :[DSocket protocol :"tcp"]])
//   {
//     printf("Open connection to the server..\n");
// 
//     if ([clnt start :addr])
//     {
//       printf("Send request to the server..\n");
// 
//       data = [clnt doRequest :"Hello" :5 :256];
// 
//       if (data != nil)
//       {
//         DText *str = [data toText];
// 
//         printf("Response from the server:%s.\n", [str cstring]);
// 
//         [str  free];
//         [data free];
//       }
//       else
//         printf("No data received from the server.\n");
//       
//       [clnt stop];
//     }
//     else
//       printf("Cound not open connection to the server.\n");
//   }
//   else
//     printf("Could not open the client.\n");
// 
//   [clnt free];                         // Cleanup
//   [addr free];
// 
//   return 0;
// }
// 

@interface DTCPClient : DObject
{
@private
  DSocket          *_local;        // the local socket
  int               _sendFlag;     // the send flags (def. 0)
  int               _recvFlag;     // the receive flags (def. 0)
  BOOL              _connected;    // is there a connection to the server ?
}

#endif


@implementation DTCPClient


//// Constructors

//
// Initialise the TCP Client
//
// @return the object
//

- (DTCPClient *) init
{
  [super init];

  _local     = [DSocket new];
  _sendFlag  = 0;
  _recvFlag  = 0;
  _connected = NO;
  
  return self;
}

//
// Initialise the TCP Client for a family and protocol
//
// @param family       the family (DSA_AF_INET, DSA_AF_UNIX, DSA_AF_INET6)
// @param protocol     the protocol to be used
//
// @return the object
//

- (DTCPClient *) init :(int) family :(int) protocol
{
  [self init];
  
  [self open :family :protocol];
  
  return self;
}


//// Deconstructor

//
// Free the TCP client object
//
// @return the object
//

- free
{
  [_local free];
  
  return [super free];
}


//// Copy related methods

//
// Do a shallow copy of the object (not implemented)
//
// @return the object
//
- shallowCopy
{
  WARNING(DW_METHOD_NOT_IMPL, "shallowCopy");
  
  return [super shallowCopy];
}


//// Member methods

//
// Return the local socket
// 
// @return a reference to the socket object
//

- (DSocket *) socket
{
  return _local;
}

//
// Check if there is a connection to the server
// 
// @return is there a connection ?
// 

- (BOOL) isConnected
{
  return _connected;
}

//
// Set the send flag
// 
// @param flag         the send flag
// 
// @return the object
// 

- (DTCPClient *) sendFlag :(int) flag
{
  _sendFlag = flag;
  
  return self;
}

//
// Return the send flag
// 
// @return the send flag
// 

- (int) sendFlag
{
  return _sendFlag;
}

//
// Set the receive flag
// 
// @param flag         the receive flag
// 
// @return the object
// 

- (DTCPClient *) recvFlag :(int) flag
{
  _recvFlag = flag;
  
  return self;
}

//
// Return the receive flag
// 
// @return the receive flag
// 

- (int) recvFlag
{
  return _recvFlag;
}


//// Main methods

//
// Open the TCP client
// 
// @param family       the family (DSA_AF_INET, DSA_AF_UNIX, DSA_AF_INET6)
// @param protocol     the protocol to be used
// 
// @return success
// 

- (BOOL) open :(int) family :(int) protocol
{
  BOOL ok = NO;

  if ([_local open :family :DSK_STREAM :protocol])
  {
    ok = YES;
  }
  
  return ok;
}
      
//
// Start the communication with the server (blocking till connection)
// 
// @param address      the address of the server
// 
// @return success
// 

- (BOOL) start :(id <DSocketAddressable>) address
{
  BOOL ok = NO;

  if (address == nil)
  {
    WARNING(DW_INVALID_ARG, "address");
  }
  else
  {
    ok = YES;

#ifdef DSA_AF_UNIX
    if ([address family] == DSA_AF_UNIX)
    {
      ok = [_local bind :address];
    }
#endif
    
    if ((ok) && ([_local connect :address]))
    {
      _connected = YES;
    }
    else
    {
      ok = NO;
    }
  }
  
  return ok;
}

//
// Send a request to the server and wait for the response (using send-
// and recvFlag)
// 
// @param request      the request to be sent
// @param length       the length of the request
// @param respLength   the (maximum) length of the response
//
// @return a (new) data object with the response (or nil for error)
//

- (DData *) doRequest :(const unsigned char *) request :(unsigned) length :(unsigned) respLength
{
  DData *response = nil;
  
  if (request == NULL)
  {
    WARNING(DW_INVALID_ARG, "request");
  }
  else if (!_connected)
  {
    WARNING(DW_OBJECT_NOT_INIT, "start");
  }
  else
  {
    int result = 0;
      
    if (length > 0)
    {
      result = [_local send :(void *) request :length :_sendFlag];
    }
    
    if (result >= 0)
    {
      response = [_local recv :respLength :_recvFlag];
    }
  }
  
  return response;
}

//
// Stop the communication with the server
// 
// @return none
// 

- (void) stop
{
  _connected = NO;
  
  [_local shutdown :DSK_SHUT_RDWR];
  
  [_local close];
}

@end

/*===========================================================================*/
